#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>

#include "net_hdr.h"
#include "pk_buff.h"
#include "wcdma.h"
#include "ap_assert.h"
#include "gtp.h"
#include "error.h"

#if defined(CONFIG_NETWORK_WCDMA)

/* #define CONFIG_DECODE_MPLS */
#define CONFIG_DECODE_VLAN


/*
 * main entry of parse packet wcdma
 */
int decode_packet_wcdma(struct pk_buff *pkb)
{
    struct ethhdr *ethdr = NULL;
    u16 proto;
    AP_ASSERT(pkb && pkb->data);

/* L2 MAC */

//struct ethhdr {
//       unsigned char   h_dest[ETH_ALEN];          destination eth addr 
//       unsigned char   h_source[ETH_ALEN];       source ether addr    
//       __be16          	    h_proto;                	   packet type ID field 
//}

    ethdr = (struct ethhdr *)pkb->data;
    pkb_pull(pkb, ETH_HLEN);
   
    proto = ntohs(ethdr->h_proto);
    if (proto < 1536) 
        return  -EPKT_NOT_INET;

    /* L2 802.1Q vlan */
#ifdef CONFIG_DECODE_VLAN
    while (ETH_P_8021Q == proto) {
        struct vlan_hdr* vlan_hdr = (struct vlan_hdr *)pkb->data;
        pkb_pull(pkb, VLAN_HLEN);
        proto = htons(vlan_hdr->h_vlan_encapsulated_proto);
    }
#endif

    if (proto != ETH_P_IP) {

#ifdef  CONFIG_DECODE_MPLS
        if (proto == ETH_P_MPLS_UC) {
            int lables = 0; 
            u32 *mpls = NULL;
            do {
                mpls = (u32 *)(pkb->data);
                pkb_pull(pkb, MPLS_HLEN);

                if (++lables >= MAX_MPLS_LABELS)
                    return -EPKT_MPLS_EXCESS;
            
            } while(!(*mpls & MPLS_STACK_BOTTOM));
        } else
            return -EPKT_NOT_IP;
#else
        return -EPKT_NOT_IP;
#endif
    } /*h_proto != ETH_P_IP*/

    /*L3*/
    pkb->iphdr = (struct iphdr *)pkb->data;
    pkb_pull(pkb, pkb->iphdr->ihl << 2);

    /*L4*/
    if (pkb->iphdr->protocol != IPPROTO_UDP)
        return -EPKT_NOT_UDP;
    else { 
        struct udphdr *udphdr = (struct udphdr *)pkb->data;
        pkb_pull(pkb, sizeof(struct udphdr));

        if (ntohs(udphdr->dest) == gtpc_port ||
            ntohs(udphdr->source) == gtpc_port)
            decode_gtp_c(pkb);
        else if (ntohs(udphdr->dest) == gtpu_port || 
            ntohs(udphdr->source) == gtpu_port)
            decode_gtp_u(pkb);
        else 
            return -EPKT_NOT_GTP;   
    }

    /* free ressamble buffer */
    if (pkb->res_buf)
        free_reassemble_buf(pkb->res_buf);

    return 0;
}

#endif /*#if defined(CONFIG_NETWORK_WCDMA)*/

